package com.cycplus.test.ui

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.util.LruCache
import com.cycplus.test.R
import com.cycplus.test.base.ContextProvider
import com.cycplus.test.extensions.asyncTask
import com.cycplus.test.utils.DateUtil
import java.io.File
import java.io.FileOutputStream
import java.io.IOException

class VideoThumbnailLoader(videoName: String, private val videoPath: String, private val frameCallback: IFrameThumbLoaded) {

    interface IFrameThumbLoaded {
        fun onFrameThumbLoaded(sec: Float, bitmap: Bitmap)
    }

    private var videoPredix = ""
    //private val frameLoader: VideoFrameLoader by lazy { VideoFrameLoader() }

    private val highLightLoadingSet = mutableSetOf<String>()
    private val diskLoadingSet = mutableSetOf<String>()
    private val loadingSet = mutableSetOf<String>()
    private val memoryCache = BitmapMemoryCache()
    private val diskCache = BitmapDiskCache()

    @Volatile
    private var frameLoaderStarting = false

    @Volatile
    private var frameLoaderStarted = false

    private val mainHandler: Handler by lazy { Handler(Looper.getMainLooper()) }

    init {
        videoPredix = videoName.substring(0, videoName.lastIndexOf("."))
        startFrameLoader()
    }

    private fun startFrameLoader() {
        mainHandler.post {
            if (videoPath.isEmpty()) {
                return@post
            }
            if (frameLoaderStarted) {
                return@post
            }
            if (!frameLoaderStarting) {
                frameLoaderStarting = true
                frameLoaderStarted = true
                //asyncTask {
                //    val startSuccess = frameLoader.startFrameSeek(videoPath,
                //        frameBitmapCallback = {
                //            onRemoteBitmapLoaded(it)
                //        },
                //        completeCallback = {
                //
                //        })
                //    frameLoaderStarting = false
                //    frameLoaderStarted = startSuccess
                //}
            }
        }
    }

    fun stop(){
        //frameLoader.endFrameSeek()
    }

    fun release(){
        memoryCache.clear()
    }

    fun loadMemoryCacheBitmap(sec: Float): Bitmap? {
        val realKey = videoPredix + "_" + DateUtil.formatFloatSeconds2String(sec) + ".jpg"
        return memoryCache.get(realKey)
    }

    fun loadDiskCacheBitmap(sec: Float) {
        val realKey = videoPredix + "_" + DateUtil.formatFloatSeconds2String(sec) + ".jpg"
        if (diskLoadingSet.contains(realKey)) {
            return
        }
        diskLoadingSet.add(realKey)
        asyncTask {
            //磁盘
            val diskCacheBitmap = diskCache.get(realKey)
            diskLoadingSet.remove(realKey)
            if (diskCacheBitmap != null) {
                memoryCache.put(realKey, diskCacheBitmap)
                frameCallback.onFrameThumbLoaded(sec, diskCacheBitmap)
            }
        }
    }

    fun loadBitmap(sec: Float, highLightFrame: Boolean) {
        val realKey = videoPredix + "_" + DateUtil.formatFloatSeconds2String(sec) + ".jpg"
        if (loadingSet.contains(realKey)) {
            return
        }
        loadingSet.add(realKey)
        if (highLightFrame) {
            highLightLoadingSet.add(realKey)
        }
        asyncTask {
            // 1. 磁盘
            val diskCacheBitmap = diskCache.get(realKey)
            if (diskCacheBitmap != null) {
                if (highLightLoadingSet.contains(realKey)) {
                    highLightLoadingSet.remove(realKey)
                } else {
                    memoryCache.put(realKey, diskCacheBitmap)
                }
                loadingSet.remove(realKey)
                frameCallback.onFrameThumbLoaded(sec, diskCacheBitmap)
                return@asyncTask
            }

            //2. 网络
            if (!frameLoaderStarted) {
                loadingSet.remove(realKey)
                highLightLoadingSet.remove(realKey)
                return@asyncTask
            }
            Thread.sleep(200)
            val bitmap = BitmapFactory.decodeResource(ContextProvider.applicationContext.resources, R.drawable.one, null)
            frameCallback.onFrameThumbLoaded(sec, bitmap)
            //frameLoader.frameSeekTo(sec)
        }
    }

    private fun onRemoteBitmapLoaded(frameBitmap: FrameBitmap) {
        val realKey = videoPredix + "_" + DateUtil.formatFloatSeconds2String(frameBitmap.timeSec) + ".jpg"
        loadingSet.remove(realKey)
        if (highLightLoadingSet.contains(realKey)) {
            highLightLoadingSet.remove(realKey)
        } else {
            memoryCache.put(realKey, frameBitmap.bitmap)
        }
        diskCache.put(realKey, frameBitmap.bitmap)
        frameCallback.onFrameThumbLoaded(frameBitmap.timeSec, frameBitmap.bitmap)
    }
}

class BitmapDiskCache {
    fun get(key: String): Bitmap? {
        val thumbnailDir = File(ContextProvider.applicationContext.cacheDir, "thumbnail")
        val file = File(thumbnailDir, key)
        if (!file.exists()) {
            return null
        }

        return try {
            BitmapFactory.decodeFile(file.absolutePath)
        } catch (e: Exception) {
            Log.e("BitmapDiskCache", "Decode error: $key, ${e.message}")
            file.delete()
            null
        }
    }

    fun put(key: String, bitmap: Bitmap) {
        val thumbnailDir = File(ContextProvider.applicationContext.cacheDir, "thumbnail")
        if (!thumbnailDir.exists()) {
            thumbnailDir.mkdirs()
        }
        val tempFile = File(thumbnailDir, "$key.tmp")
        val finalFile = File(thumbnailDir, key)

        if (finalFile.exists()) return

        try {
            FileOutputStream(tempFile).use { out ->
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out)
                out.flush()
                out.fd.sync()
            }
            tempFile.renameTo(finalFile)
        } catch (e: IOException) {
            e.printStackTrace()
            tempFile.delete()
        }
    }
}

class BitmapMemoryCache(maxSize: Int = defaultMemorySizeInKB()) {
    companion object {
        fun defaultMemorySizeInKB(): Int {
            val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
            return maxMemory / 8
        }
    }

    private val cache = object : LruCache<String, Bitmap>(maxSize) {
        override fun sizeOf(key: String, value: Bitmap): Int {
            return value.byteCount / 1024
        }

        override fun entryRemoved(evicted: Boolean, key: String, oldValue: Bitmap, newValue: Bitmap?) {
            if (evicted && !oldValue.isRecycled) {
                oldValue.recycle()
            }
        }
    }

    fun get(key: String): Bitmap? = cache.get(key)

    fun put(key: String, bitmap: Bitmap) {
        cache.put(key, bitmap)
    }

    fun random(): Bitmap? {
        val keys = cache.snapshot().keys
        if (keys.isEmpty()) {
            return null
        }
        val index = (keys.size * Math.random()).toInt()
        return cache.get(keys.elementAt(index))
    }

    fun clear() {
        val snapshot = cache.snapshot()
        for (bitmap in snapshot.values) {
            if (!bitmap.isRecycled) {
                bitmap.recycle()
            }
        }
    }
}

